home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / pct1-b.zip / MISHMASH.DOC < prev    next >
Text File  |  1990-08-06  |  31KB  |  842 lines

  1.  
  2.  
  3. The PC Assembler Tutor - Copyright (C) 1990  Chuck Nelson
  4. ______________________
  5.  
  6.  
  7.                              MISHMASH
  8.  
  9.  
  10. This document contains several assembler programs. It has no page breaks and
  11. no footnotes so you can cut the programs directly out of the text with a word
  12. processor. 
  13.  
  14.  
  15.                              BLOCK MOVE
  16.  
  17. The first subroutine does a block move from one place in memory to another. It
  18. is designed so the sorce block and the target block can be overlapping. It
  19. first calculates the total address of the sorce block and the target block. If
  20. the sorce block is below the target block the move starts at the top of the
  21. source block and moves down. If the source block is above the target block the
  22. move starts at the bottom of the source block and moves up. This makes sure
  23. that overlapping data will not be clobbered. 
  24.  
  25. This calculates the full 20 bit address. It was designed for BASIC; BASIC
  26. sometimes requires the full 20 bit address. For many languages, all you need
  27. to do is look at the offset addresses since segments cannot overlap. This is
  28. NOT true of something called the "HUGE" mode, where you need to calculate the
  29. full 20 bit address.
  30.  
  31.  
  32. +++++++++++++++++++++ << START OF PROGRAM >> ++++++++++++++++++++++
  33.  
  34. include /pushregs.mac 
  35. _TEXT SEGMENT PUBLIC 'CODE' 
  36.         ASSUME cs:_TEXT 
  37.         PUBLIC BlockMove 
  38. ; - - - - - - - - - -  
  39. ; BlockMove ( from.seg, from.off, to.seg, to.off, byte.count) 
  40. ; for BASIC 
  41. ; MOVSW is from DS:[SI] to ES:[DI] 
  42.  
  43.         FROM_SEG_ADDRESS        EQU     [bp+14] 
  44.         FROM_OFFSET_ADDRESS     EQU     [bp+12] 
  45.         TO_SEG_ADDRESS          EQU     [bp+10] 
  46.         TO_OFFSET_ADDRESS       EQU     [bp+8] 
  47.         BYTE_COUNT_ADDRESS      EQU     [bp+6] 
  48. ; - - - - - - - - - - 
  49. BlockMove proc far 
  50.         push    bp 
  51.         mov     bp, sp 
  52.         PUSHREGS  ax, bx, cx, dx, si, di, es, ds 
  53.  
  54.         ; AX:BX is the total FROM address 
  55.         ; DX:DI is the total TO address 
  56.         ; (FROM address > TO address) ->  upwards 
  57.         ; (FROM address < TO address) ->  downwards 
  58.  
  59.         ; calculate 20 bit total address 
  60.         sub     ax, ax          ; zero AX 
  61.         mov     si, FROM_SEG_ADDRESS 
  62.         mov     bx, [si]        ; from_seg to BX 
  63.         sub     dx, dx          ; zero DX 
  64.         mov     si, TO_SEG_ADDRESS 
  65.         mov     di, [si]        ; to_seg to DI 
  66.  
  67.         mov     cx, 4           ; shift 4 bytes 
  68. shift_loop: 
  69.         shl     bx, 1 
  70.         rcl     ax, 1           ; carry from BX -> AX 
  71.         shl     di, 1 
  72.         rcl     dx, 1           ; carry from DI -> DX 
  73.         loop    shift_loop 
  74.  
  75.         ; AX:BX and DX:DI now contain the total address of the 
  76.         ; segment start. Now add the offsets. 
  77.         mov     si, FROM_OFFSET_ADDRESS 
  78.         add     bx, [si] 
  79.         adc     ax, 0 
  80.         mov     si, TO_OFFSET_ADDRESS 
  81.         add     di, [si] 
  82.         adc     dx, 0 
  83.  
  84.         ; AX:BX and DX:DI are now the total addresses of the first 
  85.         ; byte to be moved. First compare AX and DX and go to the 
  86.         ; appropriate routine depending on which address is higher. 
  87.         ; If AX and DX are the same, then compare BX and DI and go 
  88.         ; to the appropriate routine. If BX = DI, the block is being 
  89.         ; moved onto itself, so just exit (there is no work to be done). 
  90.  
  91.         cmp     ax, dx 
  92.         ja      bottom_to_top   ; FROM is higher 
  93.         jb      top_to_bottom   ; TO is higher 
  94.         cmp     bx, di 
  95.         ja      bottom_to_top   ; FROM is higher 
  96.         jb      top_to_bottom   ; TO is higher 
  97.         jmp     exit 
  98.  
  99. bottom_to_top: 
  100.         mov     si, TO_SEG_ADDRESS 
  101.         mov     es, [si]        ; to_seg to ES 
  102.         mov     si, TO_OFFSET_ADDRESS 
  103.         mov     di, [si]        ; to_offset to DI 
  104.         mov     si, BYTE_COUNT_ADDRESS 
  105.         mov     cx, [si]        ; byte count to CX 
  106.         mov     si, FROM_SEG_ADDRESS 
  107.         mov     ax, [si]        ; temporary storage for new DS 
  108.         mov     si, FROM_OFFSET_ADDRESS 
  109.         mov     si, [si]        ; from_offset to SI 
  110.         mov     ds, ax          ; now move from_seg to DS 
  111.         sub     bx, bx          ; clear BX 
  112.         shr     cx, 1           ; divide by 2, remainder in CF 
  113.         rcl     bx, 1           ; move CF to low bit of BX 
  114.         cld                     ; clear DF (go up) 
  115.         rep     movsw           ; the block move (count in CX) 
  116.         and     bx, bx          ; one extra byte? 
  117.         jz      exit 
  118.         movsb                   ; move one last byte 
  119.         jmp     exit 
  120.  
  121. top_to_bottom: 
  122.         mov     si, TO_SEG_ADDRESS 
  123.         mov     es, [si]        ; to_seg to ES 
  124.         mov     si, TO_OFFSET_ADDRESS 
  125.         mov     di, [si]        ; to_offset to DI 
  126.         mov     si, BYTE_COUNT_ADDRESS 
  127.         mov     cx, [si]        ; byte count to CX 
  128.         mov     si, FROM_SEG_ADDRESS 
  129.         mov     ax, [si]        ; temporary storage for new DS 
  130.         mov     si, FROM_OFFSET_ADDRESS 
  131.         mov     si, [si]        ; from_offset to SI 
  132.         mov     ds, ax          ; now move from_seg to DS 
  133.         add     si, cx          ; move to top of block 
  134.         sub     si, 2           ; we were 1 word too far 
  135.         add     di, cx          ; move to top of block 
  136.         sub     di, 2           ; we were 1 word too far 
  137.         sub     bx, bx          ; clear BX 
  138.         shr     cx, 1           ; divide by 2, remainder in CF 
  139.         rcl     bx, 1           ; move CF to low bit of BX 
  140.         std                     ; set DF (go down) 
  141.         rep     movsw           ; the block move (count in CX) 
  142.         and     bx, bx          ; one extra byte? 
  143.         jz      exit 
  144.         inc     si              ; top byte of word 
  145.         inc     di              ; top byte of word 
  146.         movsb                   ; move one last byte 
  147.  
  148. exit: 
  149.         POPREGS  ax, bx, cx, dx, si, di, es, ds 
  150.         mov      sp, bp 
  151.         pop     bp 
  152.         ret     (10) 
  153.  
  154. BlockMove endp 
  155. ; - - - - - - - - - -  
  156. _TEXT ENDS 
  157. END   
  158.  
  159. ++++++++++++++++++++++ << END OF PROGRAM >> +++++++++++++++++++++++
  160.  
  161.  
  162.  
  163.  
  164.                     MULTIPLICATION AND DIVISION
  165.  
  166.  
  167. The following are routines for multiple word multiplication and division. They
  168. are the core routines. There must be an intermediate routine which prepares
  169. the information correctly for the core routine and then calls the core
  170. routine. Among other things, these intermediate routines must:
  171.  
  172.      1) deal with signed numbers. They must convert any negative numbers into
  173.      positive numbers and keep track of the signs. Then they must alter the
  174.      signs of the results if necessary.
  175.  
  176.      2) make copies of numbers for the core routine when the core routine will
  177.      destroy or alter the number during the calculation.
  178.  
  179.      3) make decisions about valid results for the multiplication routines. If
  180.      we multiply two numbers of length N words, then the result can be N + N
  181.      words long. What do you want to do if the result is over N words long? It
  182.      is your decision.
  183.  
  184.      4) transfer the results back to the programs if necessary. 
  185.  
  186. These are the things we did in chapter 16, and they are necessary here as
  187. well. In all the following routines you need to pay attention to the lengths.
  188. Some lengths are in BYTES and some lengths are in WORDS. Make sure you know
  189. which is which.
  190.  
  191.  
  192.  
  193.  
  194.                          BLOCK MULTIPLICATION
  195.  
  196. The first multiplication program uses block multiplication. This is simply the
  197. multiple word multiplication that you did in chapters 13 and 16. This time,
  198. instead of multiplying  n X 1  words, we will be multiplying  n X n  words.
  199. The most important thing that this routine does is minimize its work. If n =
  200. 100 words, then it is possible for the routine to do 10,000 multiplications.
  201. This takes a lot of time. If we have two 100 word numbers but the first one is
  202. 127,911 and the other one is 4,926,948,187,062 the first number has
  203. significant information in two words and the second number has significant
  204. information in three words. We only need to multiply  3 X 2 = 6 words instead
  205. of 10,000 words. As you can see, this will cut the time by a factor of over
  206. 1000. This routine requires that the result be distinct from either the
  207. multiplicand or multiplier and be  n + n  (2n) words long.
  208.  
  209. First we clear the area for the result. The next section finds the highest
  210. non-zero word of both the multiplicand and multiplier. If either is 0 the
  211. result is 0, so we exit (the result is cleared and is 0). After that comes the
  212. multiplication proper. We multiply the complete multiplicand by one multiplier
  213. word, then cycle to the next multiplier word and so on. We add each DX:AX pair
  214. to the temporary result and propagate any carry that results from the
  215. addition. The result cannot be larger than N + N words, so we will never
  216. propagate past the result area. This is as fast as you can multiply numbers on
  217. the 8086.
  218.  
  219.  
  220. +++++++++++++++++++++ << START OF PROGRAM >> +++++++++++++++++++++++
  221.  
  222. ; block multiplication using standard 8086 multiplication 
  223. ; block_multiply ( length , multiplicand, multiplier, temp_result ) 
  224.  
  225. ; length is the number of WORDS 
  226. ; length is a number, but the others are addresses. The temp_result 
  227. ; space must be (2 X length), and must be distinct from the other 
  228. ; varibles since it will be overwritten by the routine. This is 
  229. ; a far routine for C, and after setting up BP, we have: 
  230. ;      TEMP_RESULT_ADDRESS     EQU   [bp + 12] 
  231. ;      MULTIPLIER_ADDRESS      EQU   [bp + 10] 
  232. ;      MULTIPLICAND_ADDRESS    EQU   [bp + 8] 
  233. ;      DATA_LENGTH             EQU   [bp + 6] 
  234.  
  235.  
  236. INCLUDE \pushregs.mac 
  237. ; - - - - - - - - - - - - - - - - - - - - 
  238. DATASTUFF SEGMENT PUBLIC 'DATA' 
  239. multiplicand_top_address      dw   ? 
  240. multiplier_top_address        dw   ? 
  241. temp_bottom_address           dw   ? 
  242. current_multiplier_address    dw   ? 
  243. DATASTUFF  ENDS 
  244. ; - - - - - - - - - - - - - - - - - - - - 
  245. CODESTUFF SEGMENT PUBLIC 'CODE' 
  246.  
  247.      PUBLIC  block_multiply 
  248.      ASSUME CS:CODESTUFF, DS:DATASTUFF 
  249.  
  250.      TEMP_RESULT_ADDRESS     EQU   [bp + 12] 
  251.      MULTIPLIER_ADDRESS      EQU   [bp + 10] 
  252.      MULTIPLICAND_ADDRESS    EQU   [bp + 8] 
  253.      DATA_LENGTH             EQU   [bp + 6] 
  254.  
  255. ; - - - - - - - - - - 
  256. block_multiply proc far 
  257.  
  258.      push  bp 
  259.      mov   bp, sp 
  260.      pushf               ; save DF value 
  261.      PUSHREGS ax, bx, cx, dx, si, di, es 
  262.      push  ds            ; es = ds 
  263.      pop   es 
  264.  
  265.      ; clear temp_result 
  266.      mov   di, TEMP_RESULT_ADDRESS 
  267.      mov   cx, DATA_LENGTH 
  268.      shl   cx, 1        ; 2 X LENGTH is buffer length 
  269.      mov   ax, 0        ; zero for clearing 
  270.      cld                ; upwards 
  271.      rep   stosw        ; store ax 
  272.  
  273.      ; find the highest multiplicand word which is non-zero 
  274.      mov   di, MULTIPLICAND_ADDRESS 
  275.      mov   dx, DATA_LENGTH 
  276.      mov   cx, dx       ; cx = length in words 
  277.      mov   bx, dx 
  278.      dec   bx           ; first word is at offset 0 
  279.      shl   bx, 1        ; bx = top word 
  280.      add   di, bx       ; di = address of top word 
  281.      std                ; downwards 
  282.                         ; ax is still 0 
  283.      repe  scasw        ; continue as long as es:[di] is 0 
  284.      jne   first_top_found    ; found non-zero word 
  285.      jmp   exit_mult    ; multiplicand is 0 so result is 0 
  286.  
  287. first_top_found: 
  288.      add   di, 2                         ; we went 2 too far 
  289.      mov   multiplicand_top_address, di  ; address of top non-zero word 
  290.  
  291.      ; no registers have been modified except di and cx 
  292.      ; use the same ax, bx and dx values as before for multiplier. 
  293.  
  294.      ; find the highest non-zero multiplier word 
  295.      mov   di, MULTIPLIER_ADDRESS 
  296.      add   di, bx                 ; di = address of top word 
  297.      mov   cx, dx                 ; cx = length in words 
  298.                                   ; ax is still 0 
  299.      repe  scasw                  ; continue as long as es:[di] is 0 
  300.      jne   second_top_found       ; found non-zero word 
  301.      jmp   exit_mult              ; multiplier is 0 so result is 0 
  302.  
  303. second_top_found: 
  304.      add   di, 2                         ; we went 2 too far 
  305.      mov   multiplier_top_address, di    ; address of top non-zero word 
  306.  
  307.      ; the multiplication  ******************** 
  308.      mov   ax, TEMP_RESULT_ADDRESS 
  309.      mov   temp_bottom_address, ax       ; start at bottom 
  310.      mov   si, MULTIPLIER_ADDRESS 
  311.      mov   current_multiplier_address, si  ; save address 
  312.  
  313. outer_multiplication_loop: 
  314.      ; set up the registers 
  315.      mov   cx, [si]                ; move current multiplier to cx
  316.      mov   di, MULTIPLICAND_ADDRESS 
  317.      mov   bx, temp_bottom_address 
  318.  
  319. inner_multiplication_loop: 
  320.      mov   ax, cx                  ; multiplier word to ax 
  321.      mul   WORD PTR [di]           ; multiplicand - result in DX:AX 
  322.      add   [bx], ax                ; low word of multiplication 
  323.      adc   [bx+2], dx              ; high word of multiplication 
  324.      jnc   no_more_carry           ; extra work if CF=1 
  325.      mov   si, 4 
  326.      ; keep propagating the carry till CF = 0 
  327. propagate_carry: 
  328.      add   WORD PTR [bx+si], 1 
  329.      jnc   no_more_carry 
  330.      add   si, 2                   ; next word 
  331.      jmp   propagate_carry 
  332.  
  333. no_more_carry: 
  334.      add   bx, 2         ; next word of temp result 
  335.      add   di, 2         ; next word of multiplicand 
  336.      cmp   di, multiplicand_top_address  ; finished? 
  337.      ja    next_multiplier_word 
  338.      jmp   inner_multiplication_loop 
  339.  
  340. next_multiplier_word: 
  341.      mov   si, current_multiplier_address 
  342.      add   si, 2 
  343.      cmp   si, multiplier_top_address 
  344.      ja    exit_mult                ; end of multiplication 
  345.  
  346.      mov   current_multiplier_address, si  ; save address 
  347.      add   temp_bottom_address, 2   ; increment for next start 
  348.      jmp   outer_multiplication_loop 
  349.   
  350.      ; end of the multiplication  ************* 
  351.  
  352. exit_mult: 
  353.      POPREGS ax, bx, cx, dx, si, di, es 
  354.      popf              ; restore DF value 
  355.      mov   sp, bp 
  356.      pop   bp 
  357.      ret               ; a C return, so don't pop arguments. 
  358.  
  359. block_multiply  endp 
  360. ; - - - - - - - - - - 
  361. CODESTUFF ENDS 
  362. END 
  363.  
  364. ++++++++++++++++++++++ << END OF PROGRAM >> +++++++++++++++++++++++
  365.  
  366.  
  367.  
  368. If you understand all of this you can go on. The next one is even more
  369. difficult.
  370.  
  371.  
  372.  
  373.                      BINARY MULTIPLICTION
  374.  
  375. This is how the 8086 does multiplication internally. It is a series of shifts
  376. and additions. We can do the same thing with base 10 numbers.
  377.  
  378.  
  379.                24763 X 275
  380.  
  381.                     24763  ---
  382.                     24763   |
  383.                     24763   5
  384.                     24763   |
  385.                     24763  ---
  386.                    247630  ___
  387.                    247630   |
  388.                    247630   |
  389.                    247630   7
  390.                    247630   |
  391.                    247630   |
  392.                    247630  ---
  393.                   2476300  ---
  394.                   2476300   2
  395.  
  396.                 6,809,825
  397.  
  398. In the base 10 system this is tedious. In the base 2 system this works well.
  399. You either do NO addition or you do 1 addition. We start at the bottom and add
  400. (either once or not at all), then shift the whole number left one bit. We
  401. repeat this cycle till we are finished with the whole multiplier. Once again,
  402. the pivotal operation is finding the highest non-zero word before starting.
  403. This is about 5 times slower than the first method. The only reason that it is
  404. here is to prepare you for the binary division routine. 
  405.  
  406. We need to reserve an extra word above the multiplicand. If the multiplicand
  407. is 6 words long, we need 7 words for the multiplicand. The 6th word will shift
  408. into that 7th word 1 bit at a time. At the end of our 16 bit cycle, all words
  409. will have shifted up one word. 
  410.  
  411. As the multiplication progresses, the bottom words of the multiplicand will be
  412. 0 so we don't bother to add these 0 words. 
  413.  
  414. We load the multiplier into DX one word at a time. We then check this word one
  415. bit at a time. If the bit is 1 we add, if the bit is 0 we do nothing. We shift
  416. the multiplicand left 1 bit each time, whether we add or not.
  417.  
  418.  
  419.  
  420. +++++++++++++++++++++ << START OF PROGRAM >> ++++++++++++++++++++++
  421.  
  422. ; binary multiplication using shifts and addition 
  423. ; binary_multiply ( length , multiplicand, multiplier, temp_result ) 
  424.  
  425. ; length is the number of WORDS 
  426. ; length is a number, but the others are addresses. The temp_result 
  427. ; space and the multiplicand space must be ((2 X length)+1) WORDS, 
  428. ; and must be distinct from the calling variables since they will be 
  429. ; overwritten by the routine. This is a far routine for C, and after 
  430. ; setting up BP, we have: 
  431.  
  432. ;      TEMP_RESULT_ADDRESS     EQU   [bp + 12] 
  433. ;      MULTIPLIER_ADDRESS      EQU   [bp + 10] 
  434. ;      MULTIPLICAND_ADDRESS    EQU   [bp + 8] 
  435. ;      DATA_LENGTH             EQU   [bp + 6] 
  436.  
  437.  
  438. include \pushregs.mac 
  439. ; - - - - - - - - - - - - - - - - - - - - 
  440. DATASTUFF SEGMENT PUBLIC 'DATA' 
  441. multiplicand_length          dw     ? 
  442. multiplier_length            dw     ? 
  443. lowest_non_zero_word         dw     ? 
  444. DATASTUFF ENDS 
  445. ; - - - - - - - - - - - - - - - - - - - - 
  446. CODESTUFF SEGMENT PUBLIC 'CODE' 
  447.  
  448.      PUBLIC binary_multiply 
  449.      ASSUME  cs:CODESTUFF, ds:DATASTUFF 
  450.  
  451.      TEMP_RESULT_ADDRESS     EQU   [bp + 12] 
  452.      MULTIPLIER_ADDRESS      EQU   [bp + 10] 
  453.      MULTIPLICAND_ADDRESS    EQU   [bp + 8] 
  454.      DATA_LENGTH             EQU   [bp + 6] 
  455.  
  456. ; - - - - - - - - - - 
  457. binary_multiply proc far 
  458.  
  459.      push  bp 
  460.      mov   bp, sp 
  461.      pushf              ; save DF value 
  462.      PUSHREGS ax, bx, cx, dx, si, di, es 
  463.      push  ds           ; es = ds 
  464.      pop   es 
  465.  
  466.      ; clear temp buffer 
  467.      mov   di, TEMP_RESULT_ADDRESS 
  468.      mov   cx, DATA_LENGTH 
  469.      shl   cx, 1        ; 2 X LENGTH is buffer length 
  470.      mov   ax, 0 
  471.      cld                ; upwards 
  472.      rep   stosw        ; store ax 
  473.  
  474.      ; find the highest word which is non-zero 
  475.      mov   di, MULTIPLICAND_ADDRESS 
  476.      mov   dx, DATA_LENGTH 
  477.      mov   cx, dx       ; cx = length in words 
  478.      mov   bx, dx 
  479.      dec   bx 
  480.      shl   bx, 1        ; bx = top word 
  481.      add   di, bx       ; di = address of top word 
  482.      std                ; downwards 
  483.                         ; ax is still 0 
  484.      repe  scasw 
  485.      jne   first_top_found    ; found non-zero word 
  486.      jmp   exit_mult    ; multiplicand is 0 so result is 0 
  487.  
  488. first_top_found: 
  489.       ; we went 2 too far + 2 for length + 2 extra for bit shift 
  490.      add   di, 6 
  491.      sub   di, MULTIPLICAND_ADDRESS 
  492.      shr   di, 1                     ; divide by 2 
  493.      mov   multiplicand_length, di   ; length in WORDS 
  494.  
  495.      ; no registers have been modified except di and cx 
  496.      ; use the same ax, bx and dx values as before for multiplier. 
  497.  
  498.      ; find the highest non-zero word 
  499.      mov   di, MULTIPLIER_ADDRESS 
  500.      add   di, bx         ; di = address of top word 
  501.      mov   cx, dx         ; cx = length in words 
  502.                           ; ax is still 0 
  503.      repe  scasw 
  504.      jne   second_top_found    ; found non-zero word 
  505.      jmp   exit_mult      ; multiplier is 0 so result is 0 
  506.  
  507. second_top_found: 
  508.      ; we went 2 too far + 2 for length 
  509.      add   di, 4 
  510.      sub   di, MULTIPLIER_ADDRESS 
  511.      mov   multiplier_length, di        ; length in BYTES 
  512.  
  513.      ; the multiplication  ******************** 
  514.      mov   lowest_non_zero_word, 0 
  515.  
  516. multiplicand_loop: 
  517.      mov   ax, lowest_non_zero_word      ; # of words shifted 
  518.      cmp   ax, multiplier_length         ; length in bytes 
  519.      jb    multiply_a_word 
  520.      jmp   exit_mult                     ; we are through 
  521.      ; ax still has lowest word count 
  522. multiply_a_word: 
  523.      mov   si, MULTIPLIER_ADDRESS 
  524.      add   si, ax                 ; calculate where multiplier is
  525.      mov   dx, [si]               ; this is current multiplier word 
  526.      mov   cx, 16                 ; 16 adds and shifts 
  527. add_and_shift_loop: 
  528.      push  cx 
  529.      shr   dx, 1                    ; add if low bit is 1 
  530.      jnc   skip_the_addition 
  531.      mov   ax, lowest_non_zero_word     ; offset count 
  532.      mov   si, MULTIPLICAND_ADDRESS 
  533.      add   si, ax 
  534.      mov   bx, TEMP_RESULT_ADDRESS 
  535.      add   bx, ax 
  536.      mov   cx, multiplicand_length       ; length in words 
  537.      clc 
  538. inner_add_loop: 
  539.      mov   ax, [si] 
  540.      adc   [bx], ax 
  541.      inc   si               ; doesn't affect the carry flag 
  542.      inc   si 
  543.      inc   bx 
  544.      inc   bx 
  545.      loop  inner_add_loop 
  546.      adc   WORD PTR [bx], 0     ; one last carry is possible 
  547.  
  548. skip_the_addition: 
  549.      ; shift one bit to the left 
  550.      mov   si, MULTIPLICAND_ADDRESS 
  551.      add   si, lowest_non_zero_word 
  552.      mov   cx, multiplicand_length         ; length in words 
  553.      clc 
  554. shift_1_loop: 
  555.      rcl   WORD PTR [si], 1 
  556.      inc   si                     ; doesn't affect carry flag 
  557.      inc   si 
  558.      loop  shift_1_loop 
  559.  
  560.      pop   cx 
  561.      loop  add_and_shift_loop 
  562.  
  563.      add   lowest_non_zero_word, 2          ; move up one word 
  564.      jmp   multiplicand_loop 
  565.  
  566.  
  567.      ; end of the multiplication  ************* 
  568.  
  569. exit_mult: 
  570.      POPREGS ax, bx, cx, dx, si, di, es 
  571.      popf              ; restore DF value 
  572.      mov   sp, bp 
  573.      pop   bp 
  574.      ret               ; a C return, so don't pop arguments. 
  575.  
  576. binary_multiply  endp 
  577. ; - - - - - - - - - - 
  578. CODESTUFF ENDS 
  579. END 
  580.  
  581. ++++++++++++++++++++++ << END OF PROGRAM >> +++++++++++++++++++++++
  582.  
  583.  
  584.  
  585.  
  586.                        BINARY DIVISION
  587.  
  588.  
  589. This is by far the hardest to understand. The binary division routine is the
  590. opposite of the multiplication routine. We move the dividend to the remainder
  591. area since it will be modified during the routine. We shift the divisor one
  592. word past the top of the dividend (to make sure that the divisor starts out
  593. larger than the dividend) and then start the shift-subtract cycle. We shift
  594. right 1 bit and then take a look at the two numbers. If the divisor is larger
  595. than the dividend we do nothing and put a 0 bit in the quotient. If the
  596. divisor is smaller, we put a 1 bit in the quotient and subtract the divisor
  597. from the dividend. At the end, what is left of the dividend is our remainder
  598.  
  599. As usual, we only use only as many words as necessary, both for the numbers
  600. and the individual subtractions. 
  601.  
  602. This is about 5 times slower than the block multiplication. It is possible to
  603. approach the speed of the block multiplication routine by using block division
  604. routine which guesses and then modifies its guess, but it would be almost
  605. impossible to understand what the code does, so I won't show it to you.
  606.  
  607.  
  608. +++++++++++++++++++++ << START OF PROGRAM >> ++++++++++++++++++++++
  609.  
  610. ; binary division using shifts and subtraction 
  611. ; binary_divide ( length , dividend, divisor, quotient, remainder) 
  612.  
  613. ; length is the number of WORDS 
  614. ; length is a number, but the others are addresses. The divisor and 
  615. ; remainder space will be overwritten one word past the highest non- 
  616. ; zero word by the subroutine. The remainder space is cleared one word past
  617. ; its length. This is a far routine for C, and after setting up BP, we have:
  618.  
  619.      OUR_DIVIDEND_ADDRESS EQU   [bp + 14]   ; same as remainder address 
  620.      REMAINDER_ADDRESS    EQU   [bp + 14] 
  621.      QUOTIENT_ADDRESS     EQU   [bp + 12] 
  622.      DIVISOR_ADDRESS      EQU   [bp + 10] 
  623.      DIVIDEND_ADDRESS     EQU   [bp + 8] 
  624.      DATA_LENGTH          EQU   [bp + 6] 
  625.  
  626. include \pushregs.mac 
  627. ; - - - - - - - - - - - - - - - - - - - - 
  628. DATASTUFF SEGMENT PUBLIC 'DATA' 
  629.  
  630. dividend_length               dw    ? 
  631. divisor_length                dw    ? 
  632.      ; - - - - 
  633. top_divisor_address           dw    ? 
  634. bottom_divisor_address        dw    ? 
  635. top_dividend_address          dw    ? 
  636. bottom_dividend_address       dw    ? 
  637. current_quotient_address      dw    ? 
  638.      ; - - - - 
  639. shift_count                   dw    ? 
  640. quotient_bit                  dw    ? 
  641.  
  642.  
  643.  
  644. DATASTUFF ENDS 
  645. ; - - - - - - - - - - - - - - - - - - - - 
  646. CODESTUFF SEGMENT PUBLIC 'CODE' 
  647.  
  648.      PUBLIC  binary_divide 
  649.      ASSUME  cs:CODESTUFF, ds:DATASTUFF 
  650.  
  651. ; - - - - - - - - - - 
  652. binary_divide proc far 
  653.  
  654.      push bp 
  655.      mov  bp, sp 
  656.      pushf                   ; save DF value 
  657.      PUSHREGS ax, bx, cx, dx, si, di, es 
  658.      push ds             ; es = ds 
  659.      pop  es 
  660.  
  661.      ; clear quotient 
  662.      mov   ax, 0                   ; zero for clearing 
  663.      mov   dx, DATA_LENGTH         ; store for later 
  664.      mov   cx, dx 
  665.      mov   di, QUOTIENT_ADDRESS 
  666.      cld                           ; upwards 
  667.      rep   stosw 
  668.      ; move dividend to remainder area 
  669.      mov   si, DIVIDEND_ADDRESS 
  670.      mov   di, REMAINDER_ADDRESS   ; our new dividend area 
  671.      mov   cx, dx                  ; DATA_LENGTH 
  672.      rep   movsw                   ; upwards 
  673.      mov   [di], ax                ; extra 0 above dividend space 
  674.  
  675.      ; find the highest divisor word which is non-zero 
  676.      ; dx  still has DATA_LENGTH 
  677.      mov   bx, dx            ; dx = DATA_LENGTH 
  678.      dec   bx 
  679.      shl   bx, 1             ; bx = top word (in # of bytes) 
  680.      mov   di, DIVISOR_ADDRESS 
  681.      mov   bottom_divisor_address, di    ; save for later 
  682.      add   di, bx            ; di = address of top word 
  683.      mov   cx, dx            ; cx = length in words 
  684.      std                     ; downwards 
  685.      ; ax is still 0 
  686.      repe  scasw             ; look for nonzero 
  687.      jne   first_top_found   ; left loop because unequal? 
  688.      int   0                 ; divisor is 0 so divide error 
  689.  
  690. first_top_found: 
  691.      add   di, 2                         ; we went 2 too far 
  692.      mov   top_divisor_address, di       ; store for later 
  693.      sub   di, DIVISOR_ADDRESS 
  694.      add   di, 2                         ; actual length 
  695.      mov   divisor_length, di            ; length in BYTES 
  696.  
  697.      ; no registers have been modified except di and cx 
  698.      ; use the same ax, bx and dx values as before for dividend. 
  699.      ; find the highest non-zero dividend word 
  700.      ; ax is still 0 (from above) 
  701.      mov   di, OUR_DIVIDEND_ADDRESS 
  702.      mov   bottom_dividend_address, di   ; save for later 
  703.      add   di, bx      ; di = address of top word 
  704.      mov   cx, dx      ; dx = length in words 
  705.      repe  scasw       ; downwards 
  706.      jne   second_top_found        ; equal on exit? 
  707.      jmp   exit_div    ; dividend = 0 so quotient is 0, remainder is 0 
  708.  
  709. second_top_found: 
  710.      ; add 2 for overshoot & 2 for calculating length 
  711.      ; top dividend address is just past top of dividend 
  712.      add   di, 4 
  713.      mov   top_dividend_address, di   ; this is correct 
  714.      sub   di, OUR_DIVIDEND_ADDRESS 
  715.      mov   dividend_length, di    ; length in BYTES 
  716.  
  717.      ; if dividend length < divisor length, we are done 
  718.      cmp   di, divisor_length 
  719.      jae   shift_divisor 
  720.      jmp   exit_div 
  721.  
  722. shift_divisor: 
  723.      ; figure out shift count. 
  724.      ; change divisor length from bytes to words 
  725.      ; di is still dividend length 
  726.      mov   ax, di                  ; dividend_length 
  727.      mov   dx, divisor_length 
  728.      sub   ax, dx                  ; amount of shift 
  729.      add   bottom_divisor_address, ax   ; current bottom 
  730.      add   bottom_dividend_address, ax  ; current bottom 
  731.      add   ax, 2                   ; 2 extra bytes for shift 
  732.      mov   shift_count, ax         ; save shift count 
  733.      shr   dx, 1                   ; divisor length - BYTES to WORDS 
  734.      mov   cx, dx                  ; cx is amount of data to shift 
  735.      inc   dx                      ; one word extra for shift 
  736.      mov   divisor_length, dx      ; new divisor_length  (WORDS) 
  737.      ; prepare pointers for the shift 
  738.      mov   si, top_divisor_address 
  739.      mov   di, si                  ; destination pointer 
  740.      add   di, ax                  ; add the shift 
  741.      mov   top_divisor_address, di ; new top of divisor 
  742.      rep   movsw                   ; downwards 
  743.      ; zero bottom of divisor 
  744.      mov   ax, 0 
  745.      mov   cx, shift_count 
  746.      shr   cx, 1                   ; shift count in words 
  747.      rep   stosw 
  748.  
  749.      ; set up quotient info 
  750.      mov   ax, QUOTIENT_ADDRESS 
  751.      add   ax, shift_count 
  752.      sub   ax, 2                   ; address of top word 
  753.      mov   current_quotient_address, ax 
  754.      mov   quotient_bit, 0001h     ; bit to rotate 
  755.  
  756.      ; ***** the division  ***************** 
  757.  
  758. division_loop: 
  759.      cmp   shift_count, 0    ; if 0, we are done 
  760.      ja    do_shift_16 
  761.      jmp   exit_div 
  762.  
  763. do_shift_16: 
  764.      ; ++++++++++ SHIFT AND SUBTRACT LOOP ++++++ 
  765.      mov   cx, 16 
  766. shift_16_loop: 
  767.      push  cx                ; save counter 
  768.  
  769.      ; +++++++++  SHIFT ++++++++++ 
  770.      ; shift divisor one bit to the right 
  771.      ror   quotient_bit, 1 
  772.      mov   si, top_divisor_address 
  773.      mov   cx, divisor_length         ; length in words 
  774.      clc                              ; clear CF 
  775. shift_1_loop: 
  776.      rcr   WORD PTR [si], 1 
  777.      dec   si                ; doesn't affect carry flag 
  778.      dec   si 
  779.      loop shift_1_loop 
  780.  
  781.      ; +++++++++ CHECK FOR SKIP SUBTRACTION +++++++ 
  782.      ; skip subtraction if dividend < divisor 
  783.      mov   di, top_divisor_address 
  784.      mov   si, top_dividend_address 
  785.      mov   cx, divisor_length 
  786.      std                     ; decrement pointers 
  787.      repe  cmpsw             ; cmp dividend, divisor 
  788.      jb    skip_subtraction  ; dividend < divisor 
  789.  
  790.      ; +++++++++++++++ SUBTRACTION ++++++++++++++++ 
  791.      ; OR 1 into quotient 
  792.      mov   si, current_quotient_address 
  793.      mov   dx, quotient_bit 
  794.      or    [si], dx 
  795.  
  796.      mov   si, bottom_divisor_address 
  797.      mov   di, bottom_dividend_address 
  798.      mov   cx, divisor_length      ; words 
  799.      clc                           ; clear CF 
  800. subtraction_loop: 
  801.      mov   dx, [si] 
  802.      sbb   [di], dx 
  803.      inc   si 
  804.      inc   si 
  805.      inc   di 
  806.      inc   di 
  807.      loop  subtraction_loop 
  808.  
  809.      ; dividend >= divisor, so we have no final borrow 
  810.  
  811.      ; +++++++++++ AFTER SUBTRACTION ++++++++++++++++ 
  812. skip_subtraction: 
  813.      pop   cx 
  814.      loop  shift_16_loop 
  815.  
  816.      ; reset the pointers and counters for the outer loop 
  817.      sub   shift_count, 2 
  818.      sub   top_divisor_address, 2 
  819.      sub   top_dividend_address, 2 
  820.      sub   bottom_divisor_address, 2 
  821.      sub   bottom_dividend_address, 2 
  822.      sub   current_quotient_address, 2 
  823.      jmp   division_loop 
  824.  
  825.      ; end of the division  ************* 
  826.  
  827. exit_div: 
  828.      POPREGS ax, bx, cx, dx, si, di, es 
  829.      popf              ; restore DF value 
  830.      mov  sp, bp 
  831.      pop  bp 
  832.      ret            ; a C return, so don't pop arguments. 
  833.  
  834. binary_divide  endp 
  835. ; - - - - - - - - - - 
  836. CODESTUFF ENDS 
  837. END 
  838.  
  839. ++++++++++++++++++++++ << END OF PROGRAM >> +++++++++++++++++++++++
  840.  
  841.